/*
 * Copyright 2012-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.logging;

import java.lang.reflect.Constructor;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;

import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

/**
 * 日志系统的通用抽象类。
 *
 * @author Phillip Webb
 * @author Dave Syer
 * @author Andy Wilkinson
 * @author Ben Hale
 * @since 1.0.0
 */
public abstract class LoggingSystem {

	/**
	 * 用于指示使用哪个 {@link LoggingSystem} 的系统属性。
	 */
	public static final String SYSTEM_PROPERTY = LoggingSystem.class.getName();

	/**
	 * {@link #SYSTEM_PROPERTY} 的值，表示不使用任何 {@link LoggingSystem}。
	 */
	public static final String NONE = "none";

	/**
	 * 根日志记录器的名称。LoggingSystem 实现应确保该名称用于表示根日志记录器，
	 * 无论底层实现为何。
	 */
	public static final String ROOT_LOGGER_NAME = "ROOT";

	private static final LoggingSystemFactory SYSTEM_FACTORY = LoggingSystemFactory.fromSpringFactories();

	/**
	 * 返回应应用的 {@link LoggingSystemProperties}。
	 * @param environment 用于获取值的 {@link ConfigurableEnvironment}
	 * @return 应用的 {@link LoggingSystemProperties}
	 * @since 2.4.0
	 */
	public LoggingSystemProperties getSystemProperties(ConfigurableEnvironment environment) {
		return new LoggingSystemProperties(environment);
	}

	/**
	 * 重置日志系统以限制输出。此方法可能在
	 * {@link #initialize(LoggingInitializationContext, String, LogFile)} 之前调用，
	 * 以减少系统完全初始化之前的日志噪声。
	 */
	public abstract void beforeInitialize();

	/**
	 * 完全初始化日志系统。
	 * @param initializationContext 日志初始化上下文
	 * @param configLocation 日志配置位置，或 {@code null} 表示使用默认初始化
	 * @param logFile 应写入的日志输出文件，或 {@code null} 表示仅输出到控制台
	 */
	public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
	}

	/**
	 * 清理日志系统。默认实现为空。子类应重写此方法以执行特定于日志系统的清理操作。
	 */
	public void cleanUp() {
	}

	/**
	 * 返回一个在 JVM 退出时可执行的 {@link Runnable}，用于关闭此日志系统。
	 * 默认实现返回 {@code null}，表示不需要关闭操作。
	 * @return 关闭处理程序，或 {@code null}
	 */
	public Runnable getShutdownHandler() {
		return null;
	}

	/**
	 * 返回日志系统实际支持的 {@link LogLevel 日志级别} 集合。
	 * @return 支持的日志级别集合
	 */
	public Set<LogLevel> getSupportedLogLevels() {
		return EnumSet.allOf(LogLevel.class);
	}

	/**
	 * 设置指定日志记录器的日志级别。
	 * @param loggerName 要设置的日志记录器名称（{@code null} 表示根日志记录器）
	 * @param level 日志级别（{@code null} 表示移除自定义级别，使用默认配置）
	 */
	public void setLogLevel(String loggerName, LogLevel level) {
		throw new UnsupportedOperationException("Unable to set log level");
	}

	/**
	 * 返回当前 {@link LoggingSystem} 所有日志记录器的配置集合。
	 * @return 当前的配置列表
	 * @since 1.5.0
	 */
	public List<LoggerConfiguration> getLoggerConfigurations() {
		throw new UnsupportedOperationException("Unable to get logger configurations");
	}

	/**
	 * 返回指定名称的 {@link LoggingSystem} 日志记录器的当前配置。
	 * @param loggerName 日志记录器名称
	 * @return 当前的日志记录器配置
	 * @since 1.5.0
	 */
	public LoggerConfiguration getLoggerConfiguration(String loggerName) {
		throw new UnsupportedOperationException("Unable to get logger configuration");
	}

	/**
	 * 检测并返回当前使用的日志系统，支持 Logback 和 Java Logging。
	 * @param classLoader 类加载器
	 * @return 日志系统实例
	 */
	public static LoggingSystem get(ClassLoader classLoader) {
		String loggingSystemClassName = System.getProperty(SYSTEM_PROPERTY);
		if (StringUtils.hasLength(loggingSystemClassName)) {
			if (NONE.equals(loggingSystemClassName)) {
				return new NoOpLoggingSystem();
			}
			return get(classLoader, loggingSystemClassName);
		}
		LoggingSystem loggingSystem = SYSTEM_FACTORY.getLoggingSystem(classLoader);
		Assert.state(loggingSystem != null, "No suitable logging system located");
		return loggingSystem;
	}

	private static LoggingSystem get(ClassLoader classLoader, String loggingSystemClassName) {
		try {
			Class<?> systemClass = ClassUtils.forName(loggingSystemClassName, classLoader);
			Constructor<?> constructor = systemClass.getDeclaredConstructor(ClassLoader.class);
			constructor.setAccessible(true);
			return (LoggingSystem) constructor.newInstance(classLoader);
		}
		catch (Exception ex) {
			throw new IllegalStateException(ex);
		}
	}

	/**
	 * 什么也不做的 {@link LoggingSystem} 实现。
	 */
	static class NoOpLoggingSystem extends LoggingSystem {

		@Override
		public void beforeInitialize() {

		}

		@Override
		public void setLogLevel(String loggerName, LogLevel level) {

		}

		@Override
		public List<LoggerConfiguration> getLoggerConfigurations() {
			return Collections.emptyList();
		}

		@Override
		public LoggerConfiguration getLoggerConfiguration(String loggerName) {
			return null;
		}

	}

}
